using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Silverlight.Testing;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using SLNGP.Common.Extensions;
using SLNGP.Common.IoC;
using SLNGP.Core;
using SLNGP.Core.Fitness;
using SLNGP.Core.Genes;
using SLNGP.Core.GeneticOpearators;
using SLNGP.Core.GeneticPrograms;
using SLNGP.Core.Individuals;
using SLNGP.Core.Initialization;
using SLNGP.Core.Run;
using SLNGP.Core.SecondaryStrategies;
using SLNGP.Core.StopRun;
using SLNGP.Core.Test;

namespace SLNGP.Test.CoreTests.GeneticPrograms
{
    [TestClass]
    [Tag("GeneticProgram")]
    public class GeneticProgram_Test : UnitTestBase<IGeneticProgram, GeneticProgram>
    {
        #region UnitTestBase methods
        [ClassInitialize]
        public static void ClassInitialize(TestContext testContext)
        {
            UnitTestBase<IGeneticProgram, GeneticProgram>.ClassInitialize(testContext);
        }

        [ClassCleanup]
        public static void ClassCleanup()
        {
            UnitTestBase<IGeneticProgram, GeneticProgram>.ClassCleanup();
        }

        [TestInitialize]
        public override void TestInitialize()
        {
            base.TestInitialize();
        }

        [TestCleanup]
        public override void TestCleanup()
        {
            base.TestCleanup();
        }
        #endregion UnitTestBase methods

        [TestMethod]
        public void Ctor_Initialized_DefaultsAreSet()
        {
            // state - Ctor
            // action - Initialized
            // Expectation - Defaults Are Set
            Assert.IsNotNull(curElement.PossibleGenes);
            Assert.IsTrue(curElement.PossibleGenes.Count == 0);
            Assert.IsInstanceOfType(curElement.GetStrategy<IRandomProvider>(), typeof(DefaultRandomProvider));
            Assert.IsInstanceOfType(curElement.GetStrategy<ISelectRandomGeneDefinition>(), typeof(EqualProbablityRandomGeneSelection));
        }

        public interface IStubGPStrategty : IGPStrategy { }
        [TestMethod]
        public void SetGetStrategy_SetMockStrategy_GetMockStrategyReturnsIt()
        {
            // state - SetGetStrategy
            var mock = mocks.DynamicMock<IStubGPStrategty>();

            // action - SetMockStrategy
            curElement.SetStrategy<IStubGPStrategty>(mock);

            // Expectation - GetMockStrategyReturnsIt
            Assert.IsTrue(curElement.GetStrategy<IStubGPStrategty>() == mock);
        }

        [TestMethod]
        public void SetGeneticOpearator_SetMockGeneticOpearator_ParentPropertyWillBeSetCalled()
        {
            // state - SetGeneticOpearator
            var mock = mocks.StrictMock<ISelectionGeneticOpearator>();

            // set expectation
            using (mocks.Record())
                mock.Parent = curElement;

            // action - SetMockGeneticOpearator
            curElement.SetGeneticOpearator<ISelectionGeneticOpearator>(mock);

            // Expectation - ParentPropertyWillBeSetCalled
            mocks.VerifyAll();
        }

        [TestMethod]
        public void StopRunConditions_PerformGet_IsRetrievedFromIoC()
        {
            // state - StopRunConditions
            var mock = mocks.DynamicMock<IStopRunConditions>();
            IoC.Register<IStopRunConditions>(mock);

            // action - PerformGet
            var result = curElement.StopRunConditions;

            // Expectation - IsRetrievedFromIoC
            Assert.IsTrue(result == mock);
        }

        [TestMethod]
        public void StopRunConditions_PerformGet_ParentIsSetToGeneticProgram()
        {
            // state - StopRunConditions
            var mock = mocks.StrictMock<IStopRunConditions>();
            IoC.Register<IStopRunConditions>(mock);

            // set expectation
            using (mocks.Record())
                mock.Parent = curElement;

            // action - PerformGet
            var result = curElement.StopRunConditions;

            // Expectation - ParentIsSetToGeneticProgram
            mocks.VerifyAll();

        }

        [TestMethod]
        public void AddPossibleGenes_InvokeWith2Mocks_AddedToPossibleGenesCollection()
        {
            // state - AddPossibleGenes
            var mock1 = mocks.StrictMock<IGeneDefinition>();
            var mock2 = mocks.StrictMock<IGeneDefinition>();

            // action - InvokeWith2Mocks
            curElement.AddPossibleGenes(mock1, mock2);
    
            // Expectation - AddedToPossibleGenesCollection
            Assert.IsTrue(curElement.PossibleGenes.Contains(mock1));
            Assert.IsTrue(curElement.PossibleGenes.Contains(mock2));
        }

        [TestMethod]
        [ExpectedException(typeof(GeneticProgrammingException))]
        public void GetStrategy_GPStrategyDoesNotExist_ExpectionIsThrown()
        {
            // state - GetStrategy
            // action - GPStrategyDoesNotExist
            // Expectation - ExpectionIsThrown
            curElement.GetStrategy<IStubGPStrategty>();
        }

        [TestMethod]
        public void SetGeneticOpearator_AddTwoGeneticOpearatorsToTheSameInterface_TheFirstOneIsNotIncludedInTheCollectionAndtheSecondIs()
        {
            // state - SetGeneticOpearator
            var mock1 = mocks.DynamicMock<IGeneticOpearator>();
            var mock2 = mocks.DynamicMock<IGeneticOpearator>();

            // action - AddTwoGeneticOpearatorsToTheSameInterface
            curElement.SetGeneticOpearator<IGeneticOpearator>(mock1);
            curElement.SetGeneticOpearator<IGeneticOpearator>(mock2);

            // Expectation - TheFirstOneIsNotIncludedInTheCollectionAndtheSecondIs
            Assert.IsTrue(curElement.GeneticOpearators.Contains(mock2)
                && !curElement.GeneticOpearators.Contains(mock1));
        }

        [TestMethod]
        public void GetGeneticOpearator_SetMockGeneticOpearator_GetReturnsIt()
        {
            // state - GetGeneticOpearator
            var mock = mocks.DynamicMock<IGeneticOpearator>();

            // action - SetMockGeneticOpearator
            curElement.SetGeneticOpearator<IGeneticOpearator>(mock);

            // Expectation - GetReturnsIt
            Assert.IsTrue(curElement.GetGeneticOpearator<IGeneticOpearator>() == mock);

        }

        [TestMethod]
        [ExpectedException(typeof(GeneticProgrammingException), "GeneticProgram.FitnessFunction was not specified. Please set a fitness function that evaulates individuals.")]
        public void Run_MissingFitnessFunction_ExpectionIsThrown()
        {
            // state - Run
            // action - MissingFitnessFunction
            // Expectation - ExpectionIsThrown
            curElement.RunUntilStopCondition();
        }

        [TestMethod]
        [ExpectedException(typeof(GeneticProgrammingException), "GeneticProgram.FirstGenerationInitialization was not specified. Please specify how to initialize the individual population.")]
        public void RunWithFitnessFunction_MissingGenerationInitialization_ExpectionIsThrown()
        {
            // state - RunWithFitnessFunction
            curElement.FitnessFunction = mocks.DynamicMock<IFitnessFunction>();

            // action - MissingFitnessFunction
            // Expectation - ExpectionIsThrown
            curElement.RunUntilStopCondition();
        }

        [TestMethod]
        [ExpectedException(typeof(GeneticProgrammingException), "GeneticProgram.StopRunConditions was not speicified. Please specify a stop condition for the genetic program.")]
        public void RunWithFitnessFunctionAndGenerationInitialization_MissingStopConditions_ExpectionIsThrown()
        {
            // state - RunWithFitnessFunctionAndGenerationInitialization
            curElement.FitnessFunction = mocks.DynamicMock<IFitnessFunction>();
            curElement.FirstGenerationInitialization = mocks.DynamicMock<IFirstGenerationInitialization>();

            // action - MissingFitnessFunction
            // Expectation - ExpectionIsThrown
            curElement.RunUntilStopCondition();
        }

        [TestMethod]
        [SatisfyRunConditionsAndStopAfter1Iteration]
        public void RunWithMockFirstGeneration_Run_GenerationIsAddedToLastRunResults()
        {
            // state - RunWithMockFirstGeneration
            var mock = mocks.StrictMock<IFirstGenerationInitialization>();
            var mockGeneration = mocks.DynamicMock<IGeneration>();
            curElement.FirstGenerationInitialization = mock;

            // Expectation - GenerationIsAddedToLastRunResults
            using (mocks.Record())
                Expect.Call(mock.GetFirstGeneration()).Return(mockGeneration);

            // action - Run
            // verify
            using(mocks.Playback())
            {
                curElement.RunUntilStopCondition();
                Assert.IsTrue(curElement.LastRunResults.Generations[0] == mockGeneration);
            }
         }

        [TestMethod]
        [SatisfyRunConditionsAndStopAfter1Iteration]
        public void RunWithMocksAttribute_Returns2EmptyGenerations()
        {
            // state - Empty
            // action - Run
            curElement.RunUntilStopCondition();

            // Expectation - Returns2EmptyGenerations
            Assert.IsTrue(curElement.LastRunResults.Generations.Count == 2);
            Assert.IsTrue(curElement.LastRunResults.Generations[0].Count == 0);
            Assert.IsTrue(curElement.LastRunResults.Generations[1].Count == 0);
        }

        [TestMethod]
        [SatisfyRunConditionsAndStopAfter1Iteration(SetStopConditions = false)]
        public void RunWithStopConditionsOfStopAfter4Iterations_Run_Returns5Generations()
        {
            // state - WithStopConditionsOfStopAfter4Iterations
            var mock = mocks.DynamicMock<IStopRunConditions>();
            using (mocks.Record())
            {
                int i = 0;
                Expect.Call(mock.CanStop()).Do((Func<bool>) (() =>
                                                     {
                                                         i++;
                                                         return (i == 4);
                                                     }));
            }

            using (mocks.Playback())
            {
                curElement.StopRunConditions = mock;

                // action - Run
                curElement.RunUntilStopCondition();

                // Expectation - Returns5Generations
                Assert.IsTrue(curElement.LastRunResults.Generations.Count == 5);
            }
        }

        [TestMethod]
        [SatisfyRunConditionsAndStopAfter1Iteration]
        public void RunWithFirstMockGeneration_FitnessFunctionIsCalledWithGeneration()
        {
            // state - RunWithFirstMockGeneration
            var mock = mocks.StrictMock<IFirstGenerationInitialization>();
            curElement.FirstGenerationInitialization = mock;
            var mockGeneration = mocks.DynamicMock<IGeneration>();

            using (mocks.Record())
            {
                // actio
                Expect.Call(mock.GetFirstGeneration()).Return(mockGeneration);

                // expectation - FitnessFunctionIsCalledWithGeneration
                curElement.FitnessFunction.Evaluate(mockGeneration);
            }

            curElement.RunUntilStopCondition();

            // verify
            mocks.VerifyAll();
        }

        [TestMethod]
        [SatisfyRunConditionsAndStopAfter1Iteration(SetStopConditions = false)]
        public void RunWithStopConditionsOfStopAfter4Iterations_Run_FitnessFunctionInvoked4Times()
        {
            // state - WithStopConditionsOfStopAfter4Iterations
            var mock = mocks.DynamicMock<IStopRunConditions>();
            using (mocks.Record())
            {
                int i = 0;
                Expect.Call(mock.CanStop()).Do((Func<bool>)(() =>
                {
                    i++;
                    return (i == 4);
                }));

                // Expectation - FitnessFunctionInvoked4Times
                curElement.FitnessFunction.Evaluate(null);
                LastCall.IgnoreArguments().Repeat.Times(4);
            }

            using (mocks.Playback())
            {
                curElement.StopRunConditions = mock;

                // action - Run
                curElement.RunUntilStopCondition();

                //verify
            }
        }

        [TestMethod]
        [SatisfyRunConditionsAndStopAfter1Iteration(SetStopConditions = false)]
        public void RunWithStopConditionsOfStopAfter4Iterations_AddGeneticOpearator_GeneticOpearatorInvoked4Times()
        {
            // state - WithStopConditionsOfStopAfter4Iterations
            var mock = mocks.DynamicMock<IStopRunConditions>();
            var geneticOpearatorMock = mocks.DynamicMock<IGeneticOpearator>();
            using (mocks.Record())
            {
                int i = 0;
                Expect.Call(mock.CanStop()).Do((Func<bool>)(() =>
                {
                    i++;
                    return (i == 4);
                }));

                // Expectation - GeneticOpearatorInvoked4Times
                geneticOpearatorMock.Execute();
                LastCall.Repeat.Times(4);
            }

            using (mocks.Playback())
            {
                curElement.StopRunConditions = mock;
                curElement.GeneticOpearators.Add(geneticOpearatorMock);

                // action - Run
                curElement.RunUntilStopCondition();

                //verify
            }
        }

        [TestMethod]
        [SatisfyRunConditionsAndStopAfter1Iteration(SetStopConditions = false)]
        public void RunWithStopConditionsOfStopAfter2Iterations_Add2MockGeneticOpearators_InvokedInOrder()
        {
            // state - WithStopConditionsOfStopAfter4Iterations
            var mock = mocks.DynamicMock<IStopRunConditions>();
            var geneticOpearatorMock1 = mocks.DynamicMock<IGeneticOpearator>();
            var geneticOpearatorMock2 = mocks.DynamicMock<IGeneticOpearator>();
            using (mocks.Record())
            {
                int i = 0;
                Expect.Call(mock.CanStop()).Do((Func<bool>)(() =>
                {
                    i++;
                    return (i == 2);
                }));
            }

            using(mocks.Ordered())
            {
                // Expectation - InvokedInOrder
                geneticOpearatorMock1.Execute();
                geneticOpearatorMock2.Execute();
                geneticOpearatorMock1.Execute();
                geneticOpearatorMock2.Execute();
            }

            using (mocks.Playback())
            {
                curElement.StopRunConditions = mock;
                curElement.GeneticOpearators.Add(geneticOpearatorMock1);
                curElement.GeneticOpearators.Add(geneticOpearatorMock2);

                // action - Run
                curElement.RunUntilStopCondition();

                //verify
            }
        }
    }

    public class SatisfyRunConditionsAndStopAfter1IterationAttribute : TestInitializeActionAttribute
    {
        public SatisfyRunConditionsAndStopAfter1IterationAttribute()
        {
            SetStopConditions = true;
        }

        public bool SetStopConditions { get; set; }

        public override void Execute(object unitTest)
        {
            var test = unitTest.As<GeneticProgram_Test>();

            if (SetStopConditions)
            {
                var stopRunConditionsMock = test.mocks.DynamicMock<IStopRunConditions>();
                using (test.mocks.Record()) 
                    Expect.Call(stopRunConditionsMock.CanStop()).Return(true);
                test.curElement.StopRunConditions = stopRunConditionsMock;
            }

            IoC.Register<IGeneticProgrammingRunResults>(new GeneticProgrammingRunResults());

            test.curElement.FitnessFunction = test.mocks.DynamicMock<IFitnessFunction>();
            test.curElement.FirstGenerationInitialization = test.mocks.DynamicMock<IFirstGenerationInitialization>();
        }
    }
}
